#include "shell.h"
#include "execute.h"
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>

pid_t shPid;

int execute(t_CmdLine *CmdLine)
{
	if (CmdLine->cmd == NULL)
		return 0;
	shPid = getpid();
	int fd[2], tmpfd[2], stdfd[2];
	pid_t childPid;

	pipe(stdfd);
	printf("[%d]\n", stdfd[0]);
	dup2(STDIN_FILENO, stdfd[0]);
	dup2(STDOUT_FILENO, stdfd[1]);

	if (CmdLine->next == NULL)
	{
		if (isBuiltIn(CmdLine->cmd))
			RunBuiltIn(CmdLine->cmd);
		else
		{
			if ((childPid = fork()) == 0)
			{
				close(stdfd[0]);
				close(stdfd[1]);
				executeSingleCmd(CmdLine->cmd);
			}
			else
			{
				close(stdfd[0]);
				close(stdfd[1]);
			}
		}
	}
	else
	{
		// handle the first
		pipe(fd);
		if ((childPid = fork()) == 0)
		{
			close(stdfd[0]);
			close(stdfd[1]);
			dup2(fd[1], STDOUT_FILENO);
			close(fd[1]);
			close(fd[0]);
			if (isBuiltIn(CmdLine->cmd))
			{
				RunBuiltIn(CmdLine->cmd);
				exit(0);
			}
			else
				executeSingleCmd(CmdLine->cmd);
		}
		else
		{
			tmpfd[0] = fd[0];
			tmpfd[1] = fd[1];
			close(fd[1]);
			CmdLine = CmdLine->next;
	
			// handle the rest but last
			while (CmdLine->next)
			{
			}
			
			// handle the last
			if ((childPid = fork()) == 0)
			{
				dup2(tmpfd[0], STDIN_FILENO);
				close(tmpfd[0]);
				dup2(stdfd[1], STDOUT_FILENO);
				close(stdfd[1]);
				close(stdfd[0]);
				if (isBuiltIn(CmdLine->cmd))
				{
					RunBuiltIn(CmdLine->cmd);
					exit(0);
				}
				else
					executeSingleCmd(CmdLine->cmd);
			}
			else
			{
				wait(NULL);
				close(fd[0]);
			}
		}
	}
	wait(NULL);
	close(stdfd[0]);
	close(stdfd[1]);
	return 0;
}

void RunBuiltIn(t_Command *command)
{
	int fd_in, fd_out, fd_err;
	if (command->inFile != NULL)
	{
		fd_in = open(command->inFile->fileName, O_RDONLY);
		dup2(fd_in, STDIN_FILENO);
	}
	if (command->outFile != NULL)
	{
		fd_out = open(command->outFile->fileName, O_RDWR | O_CREAT);
		dup2(fd_out, STDOUT_FILENO);
	}
	if (command->errMsg != NULL)
	{
		fd_err = open(command->errMsg->fileName, O_RDWR | O_CREAT);
		dup2(fd_err, STDERR_FILENO);
	}
	if (strncmp(command->cmd, "exit", 4) == 0)
		handleExit(1);
	if (strncmp(command->cmd, "path", 4) == 0)
		handlePath(command);
	if (strncmp(command->cmd, "cd", 2) == 0)
		handleCd(command);

}

int isBuiltIn(t_Command *command)
{
	if (strncmp(command->cmd, "exit", 4) == 0)
		return 1;
	if (strncmp(command->cmd, "path", 4) == 0)
		return 1;
	if (strncmp(command->cmd, "cd", 2) == 0)
		return 1;
	return 0;
}

int executeSingleCmd(t_Command *command)
{
	int fd_in, fd_out, fd_err;
	if (command->inFile != NULL)
	{
		fd_in = open(command->inFile->fileName, O_RDONLY);
		dup2(fd_in, STDIN_FILENO);
	}
	if (command->outFile != NULL)
	{
		fd_out = open(command->outFile->fileName, O_RDWR | O_CREAT, 436);
		dup2(fd_out, STDOUT_FILENO);
	}
	if (command->errMsg != NULL)
	{
		fd_err = open(command->errMsg->fileName, O_RDWR | O_CREAT);
		dup2(fd_err, STDERR_FILENO);
	}

	{
		t_args *ptr;
		int argc;
		ptr = command->arguments;
		argc = 0;
		while (ptr)
		{
			argc++;
			ptr = ptr->next;
		}
		char *argv[argc + 2];
		ptr = command->arguments;
		int i;
		argv[0] = malloc(sizeof(char) * 4096);
		strcpy(argv[0], command->cmd);
		i = 1;
		while (ptr)
		{
			argv[i] = malloc(sizeof(char) * 4096);
			strcpy(argv[i], ptr->arg);
			i++;
			ptr = ptr->next;
		}
		argv[i] = NULL;

		char *fullpath;
		fullpath = malloc(sizeof(char) * 4400);

		strcpy(fullpath, "");
		strcat(fullpath, currentPath);
		strcat(fullpath, "/");
		strcat(fullpath, command->cmd);
		execv(fullpath, argv);

		strcat(fullpath, command->cmd);
		strcpy(fullpath, "");
		strcat(fullpath, command->cmd); 
		execv(fullpath, argv);
		
		path *pathHeader;
		pathHeader = workingPath;
		while (pathHeader)
		{
			strcpy(fullpath, "");
			strcat(fullpath, pathHeader->pathVal);
			strcat(fullpath, "/");
			strcat(fullpath, command->cmd); 
			execv(fullpath, argv);
		}
		printf("%s : Command not found\n", command->cmd);
		exit(0);
	}
}

// 1 for success. -1 for error
int handleExit(int mode)
{
	exit(0);
}

void sig_usr(int signo)
{
	if (signo == SIGUSR1)
	{
		signal(SIGUSR1, sig_usr);
		handleExit(1);
	}
	return;
}

// 2 for success. -1 for error
int handleCd(t_Command *command)
{
	if (command->arguments == NULL)
	{
		chdir(homeDir);
		getcwd(currentPath, PATH_LENGTH);
	}
	else
		if (chdir(command->arguments->arg) != 0)
			printf("Dir: [%s]\tNo such directory!\n", command->arguments->arg);
		else
			getcwd(currentPath, PATH_LENGTH);
	return 2;
}

// 3 for success. -1 for error
int handlePath(t_Command *command)
{
	if (command->arguments == NULL)
	{
		// display current paths
		path *tmp;
		tmp = workingPath;
		printf("  ");
		while (tmp)
		{
			printf("%s", tmp->pathVal);
			tmp = tmp->next;
			if (tmp)
				printf(":");
		}
		printf("\n");
	}
	else
	{
		// the first parameter has to be "+" or "-"
		if (strncmp(command->arguments->arg, "+", 1) != 0 && strncmp(command->arguments->arg, "-", 1) != 0)
			printf("Syntax error for \"path\" command.\n");
		else
		{
			if (strncmp(command->arguments->arg, "+", 1) == 0)
			{
				// appending a new path to the path list
				path *tmp;
				tmp = workingPath;
				if (tmp == NULL)
				{
					workingPath = malloc(sizeof(path));
					tmp = workingPath;
					tmp->next = NULL;
					tmp->pathVal = malloc(sizeof(char) * PATH_LENGTH);
					strcpy(tmp->pathVal, command->arguments->next->arg);
				}
				else
				{
					while (tmp->next != NULL)
						tmp = tmp->next;
					tmp->next = malloc(sizeof(path));
					tmp = tmp->next;
					tmp->next = NULL;
					tmp->pathVal = malloc(sizeof(char) * PATH_LENGTH);
					strcpy(tmp->pathVal, command->arguments->next->arg);
				}
			}
			else
			{
				// removing a path from the path list
				path *ptr;
				path *prev;
				ptr = workingPath;
				while (workingPath && (strcmp(command->arguments->next->arg, workingPath->pathVal) == 0))
				{
					workingPath = workingPath->next;
					free(ptr);
					ptr = workingPath;
				}
				if (ptr != NULL)
				{
					prev = ptr;
					ptr = ptr->next;
					while (ptr)
					{
						if (strcmp(command->arguments->next->arg, ptr->pathVal) == 0)
						{
							prev->next = ptr->next;
							free(ptr);
							ptr = prev->next;
						}
						else
						{
							prev = ptr;
							ptr = ptr->next;
						}
					}
				}
			}
		}
	}
	return 3;
}
